package org.dromara.system.controller;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.encrypt.utils.EncryptUtils;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.bo.node.NodePushUpdateBo;
import org.dromara.system.domain.bo.powerpay.PowerUpdateBo;
import org.dromara.system.kernel.channel.*;
import org.dromara.system.constants.OrderConstants;
import org.dromara.system.domain.*;
import org.dromara.system.domain.bo.*;
import org.dromara.system.domain.bo.aparams.AInnerPayBo;
import org.dromara.system.domain.bo.aparams.AOrderBo;
import org.dromara.system.domain.bo.companychannel2.CpCh2UpdateBo;
import org.dromara.system.domain.bo.cone.ConeUpdateBo;
import org.dromara.system.domain.bo.config.ApiConfig;
import org.dromara.system.domain.bo.cparams.OrderUpdateBo;
import org.dromara.system.domain.bo.xiangpay.XiangUpdateBo;
import org.dromara.system.domain.vo.*;
import org.dromara.system.kernel.*;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.*;
import org.dromara.system.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;

@RequiredArgsConstructor
@RestController
@RequestMapping("/external")
public class ExternalController extends BaseController {

    private final OrderInsertKernel orderInsertKernel;
    private final OrderUpdateKernel orderUpdateKernel;
    private final OrderInnerPayKernel innerPayKernel;
    private final IPayOrderLogService orderLogService;
    private final IPayAppService payAppService;
    private final IPayGroupTypeService payGroupTypeService;
    private final IPayOrderService payOrderService;
    private final IPayLinkService payLinkService;
    private final IPayCountryService payCountryService;
    private final IPayZoneService payZoneService;
    private final ConeOrderKernel coneOrderKernel;
    private final XiangPayOrderKernel xiangPayOrderKernel;
    private final CompanyChannel2Kernel companyChannel2Kernel;
    private final CompanyChannel4Kernel companyChannel4Kernel;
    private final CompanyChannel999Kernel companyChannel999Kernel;
    private final PowerKernel powerKernel;
    private final IPayAccountService payAccountService;
    private final PayVersion2Kernel payVersion2Kernel;
    private final SendEmail2Kernel sendEmail2Kernel;
    private final SysUserMapper sysUserMapper;
    private final GroupLogInsertKernel groupLogInsertKernel;
    @Value("${gatewayDomain}")
    public String gatewayDomain;
    @Value("${pluginName}")
    public String pluginName;

    /**
     * 获取支付方式
     */
    @Log(title = "获取支付方式", businessType = BusinessType.OTHER)
    @PostMapping("/payType")
    public R<?> payType(@RequestBody PayGroupTypeQueryBo bo, HttpServletRequest request) {

        long time = System.currentTimeMillis();
        String secretKey = request.getHeader(OrderConstants.secret_key);
        String appId = request.getHeader(OrderConstants.app_id);
        String xt_plugin = request.getHeader(OrderConstants.xt_plugin);
        bo.setAppId(Long.valueOf(appId));

        //查询应用 可优化查询redis
        PayApp app = payAppService.selectById(bo.getAppId());

        CompletableFuture<R<?>> future = CompletableFuture.supplyAsync(() -> {
            if (app == null || app.getIsDelete() == 1) {
                return R.fail("The appId is incorrect");//appId有误
            }
            if (Objects.equals(bo.getVersion(), "v2")) {
                return payVersion2Kernel.payType(new PayGroupTypeQuery2Bo(bo.getCountry()), request);
            }

            if (StringUtils.isEmpty(secretKey)) {
                return R.fail("client secret key is empty");
            }

            //判断total是否是一个合法的金额字段
            BigDecimal total = null;
            try {
                // 尝试将字符串转换为BigDecimal
                total = new BigDecimal(bo.getTotal());
            } catch (Exception ignore) {
            }
            if (total == null || total.compareTo(BigDecimal.ZERO) <= 0) {
                return R.fail("The total amount is invalid");
            }

            //校验应用密匙
            if (!app.getAppSecret().equals(secretKey)) {
                return R.fail("The appSecret  is incorrect, params field [Secret-Key]");//应用密匙【Secret-Key】有误
            }
            if (app.getGroupId() == null) {
                return R.fail("No bind pay group");
            }
            if (Objects.equals(xt_plugin, pluginName) && app.getAgentId() != null) {
                //校验此应用是否能接入YY插件
                SysUser user = sysUserMapper.selectById(app.getAgentId());
                if (user != null && !Objects.equals(user.getNexpay(), 1)) {
                    return R.fail("This app is not allowed to access the plugin");
                }
            }
            //获取应用配置的支付平台
            return payGroupTypeService.selectPayGroupType(app.getGroupId(), null, bo);
        });

        R<?> r = R.fail();
        try {
            r = future.get();
        } catch (Exception ignored) {}

        //记录日志
        groupLogInsertKernel.insert(app, "获取支付方式", JSONObject.toJSONString(bo), r, System.currentTimeMillis() - time);

        return r;
    }

    /**
     * 推送订单
     */
    @Log(title = "推送订单", businessType = BusinessType.INSERT)
    @PostMapping("/push")
    public R<?> push(@RequestBody AOrderBo bo, HttpServletRequest request) {

        if (Objects.equals(bo.getVersion(), "v2")) {
            return payVersion2Kernel.push(bo, request);
        }

        String secretKey = request.getHeader(OrderConstants.secret_key);
        String appId = request.getHeader(OrderConstants.app_id);
        String xt_plugin = request.getHeader(OrderConstants.xt_plugin);
        bo.setAppId(Long.valueOf(appId));
        bo.setXtPlugin(xt_plugin);

        if (StringUtils.isEmpty(secretKey)) {
            return R.fail("client secret key is empty");
        }

        return orderInsertKernel.pushOrder(bo, secretKey);
    }

    /**
     * 节点更新订单
     */
    @Log(title = "节点更新订单", businessType = BusinessType.INSERT)
    @PostMapping("/nodeUpdate")
    public R<?> nodeUpdate(@RequestBody NodePushUpdateBo bo) {
        return orderInsertKernel.nodeUpdate(bo);
    }

    /**
     * 更新订单
     */
    @Log(title = "更新订单", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    public R<?> update(@Validated(AddGroup.class) @RequestBody OrderUpdateBo bo) {

        if (!RedisUtils.tryLock("update_order_a_" + bo.getOrder_id(), 10, 10)) {
            return R.fail("还有未执行完成的订单更新任务");
        }

        PayOrder order = payOrderService.selectById(bo.getOrder_id());
        if (order == null) {
            return R.fail("Order does not exist");
        }

        //新增一条订单日志
        String resStr = JSONObject.toJSONString(bo);
        boolean hasRequest = StringUtils.isNotEmpty(bo.getRequest());
        boolean hasResponse = StringUtils.isNotEmpty(bo.getResponse());
        if (hasRequest || hasResponse) {
            try {
                JSONObject updatedBo = JSONObject.from(bo);
                if (hasRequest) {
                    updatedBo.put("request", JSONObject.parseObject(bo.getRequest()));
                }
                if (hasResponse) {
                    updatedBo.put("response", JSONObject.parseObject(bo.getResponse()));
                }
                resStr = updatedBo.toString();
            } catch (Exception ignore) {
            }
        }

        R<?> r = orderUpdateKernel.updateOrder(order, bo, resStr);
        RedisUtils.unlock("update_order_a_" + bo.getOrder_id());
        return r;
    }

    /**
     * 短链码获取配置信息
     */
    @Log(title = "短链码获取配置信息", businessType = BusinessType.OTHER)
    @PostMapping("/codeInfo")
    public R<?> codeInfo(@RequestBody JSONObject json) {
        if (json == null || !json.containsKey("code")) {
            return R.fail("参数无效");
        }
        return payLinkService.getCodeInfo(json.getString("code"));
    }

    /**
     * 短链码获取配置信息
     */
    @PostMapping("/country")
    public R<?> country(@RequestBody PayCountryBo bo) {
        List<PayCountryVo> list = payCountryService.queryList(bo);
        return R.ok(list);
    }

    /**
     * 短链码获取配置信息
     */
    @PostMapping("/zone")
    public R<?> zone(@RequestBody PayZoneBo bo) {
        List<PayZoneVo> list = payZoneService.queryList(bo);
        return R.ok(list);
    }

    @Log(title = "短链获取支付链接", businessType = BusinessType.INSERT)
    @PostMapping({"/link"})
    public R<?> payLink(@RequestBody JSONObject params) {
        String code = params.getString("code");
        if (StringUtils.isEmpty(code)) {
            return R.fail("code cannot be empty");
        }
        Long accountId = params.getLong("accountId");
        if (accountId == null) {
            return R.fail("account cannot be empty");
        }
        PayLinkVo link = payLinkService.selectByCode(code);
        if (link == null) {
            return R.fail("link does not exist");
        }

        if (!Objects.equals(link.getStatus(), 1)) {
            return R.fail("link is unavailable!");
        }
        if (link.getExpireTime() != null && link.getExpireTime().before(new Date())) {
            return R.fail("The link has expired");
        }

        PayApp app = payAppService.selectById(link.getAppId());
        if (app == null || app.getIsDelete() == 1) {
            return R.fail("The app does not exist");
        }

        AOrderBo.Address address = params.to(AOrderBo.Address.class);
        AOrderBo bo = new AOrderBo();
        bo.setAppId(link.getAppId());
        bo.setCurrency(link.getCurrency());
        bo.setTotal(link.getTotal());
        bo.setProductDataList(JSONArray.parseArray(link.getProduct(), AOrderBo.ProductData.class));
        bo.setSuccessUrl(link.getSuccessUrl());
        bo.setCancelUrl(link.getCancelUrl());
        bo.setFailureUrl(link.getFailureUrl());
        bo.setBilling(address);
        bo.setShipping(address);
        bo.setOrderAid(orderInsertKernel.generateOrderNumber());
        bo.setCustomerIpAddress(ServletUtils.getClientIP());
        bo.setCreateLink(1);
        bo.setLinkCode(link.getCode());
        bo.setGroupAccountId(accountId);

        //新增一条订单日志
        PayOrderLog log = orderLogService.saveLog(null, null, orderInsertKernel.generateOrderNumber(), bo.getOrderAid(), "推送订单", JSONObject.toJSONString(bo, JSONWriter.Feature.WriteMapNullValue), null, System.currentTimeMillis());

        bo.setOrderNo(log.getOrderNo());
        R<PushOrderVo> result = orderInsertKernel.pushOrder(bo, app.getAppSecret(), log);

        PushOrderVo pushVo = result.getData();
        PayOrder order = null;
        ApiConfig.PayMethod method = null;
        if (pushVo != null) {
            order = pushVo.getOrder();
            method = pushVo.getPayMethod();
        }

        JSONObject data = new JSONObject();
        R<?> res;
        if (R.isSuccess(result) && order != null) {
            data.put("orderNo", order.getOrderNo());
            data.put("innerLink", "");
            data.put("link", "");
            data.put("checkoutId", StringUtils.isEmpty(order.getLink()) ? order.getSessionId() : "");
            data.put("createTime", order.getCreateTime());
            if (method != null && Objects.equals(method.getInnerType(), 3)) {
                data.put("innerLink", order.getLink());
                data.put("link", "");
            } else {
                data.put("link", order.getLink());
            }
            res = R.ok(data);
        } else {
            res = R.fail(result.getMsg());
        }

        orderLogService.updateLog(log.getLogId(), order != null ? order.getOrderId() : null, JSONObject.toJSONString(res, JSONWriter.Feature.WriteMapNullValue), System.currentTimeMillis() - log.getTime());

        return res;
    }

    /**
     * 内嵌支付
     *
     * @return R
     */
    @Log(title = "内嵌支付", businessType = BusinessType.OTHER)
    @PostMapping("/innerPay")
    public R<?> innerPay(@Validated(AddGroup.class) @RequestBody AInnerPayBo innerPayBo, HttpServletRequest request) {
        String referer = request.getHeader("referer");
        String sign = request.getHeader(OrderConstants.token_name);

        if (StringUtils.isEmpty(sign) || StringUtils.isEmpty(referer)) {
            return R.fail("sign validation failed");
        }

        String str = referer.split("html")[0] + "html" + JwtUtils.secretStr;
        if (!sign.equals(EncryptUtils.encryptByMd5(str))) {
            return R.fail("sign validation failed!");
        }

//        String msg = CardValidUtils.validateCardNumber(innerPayBo.getNumber());
//        if (StringUtils.isNotEmpty(msg)) {
//            return R.fail(msg);
//        }
//        msg = CardValidUtils.validateCardExpiry(innerPayBo.getExpiry());
//        if (StringUtils.isNotEmpty(msg)) {
//            return R.fail(msg);
//        }
//        msg = CardValidUtils.validateCardCVC(innerPayBo.getCvc());
//        if (StringUtils.isNotEmpty(msg)) {
//            return R.fail(msg);
//        }

        String token = innerPayBo.getToken();
        try {
            if (!RedisUtils.tryLock("inner_order_pay_" + token, 10, 10)) {
                return R.fail("order is being processed, please try again later");//订单处理中, 请稍后重试
            }

            String orderId = EncryptUtils.decryptBySm4(token, EncryptUtils.PRIVATE_KEY);
            if (StringUtils.isEmpty(orderId)) {
                return R.fail("order not exist");
            }
            PayOrder order = payOrderService.selectById(Long.parseLong(orderId));
            if (order == null) {
                return R.fail("order not exist");
            }

            if (order.getStatus() == 2) {
                //订单已支付
                return R.fail("order has been paid");
            }

            //内嵌支付请求处理
            return innerPayKernel.innerPay(order, innerPayBo);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("token error");
        } finally {
            //解锁
            RedisUtils.unlock("inner_order_pay_" + token);
        }
    }

    /**
     * CQ-1回调更新订单
     */
    @Log(title = "CQ-1更新订单", businessType = BusinessType.UPDATE)
    @RequestMapping("/updateCq")
    public JSONObject updateCq(ConeUpdateBo cBo) {
        return coneOrderKernel.updateOrder(cBo);
    }

    /**
     * adyen支付回调
     *
     * @return R
     */
    @Log(title = "adyen支付回调", businessType = BusinessType.OTHER)
    @PostMapping("/adyenBack")
    public R<?> adyenBack(@RequestBody JSONObject json) {
        if (json.isEmpty()) {
            return R.fail("params is null");
        }

        Long orderId = null;
        Integer code = null;
        if (json.containsKey("notificationItems")) {
            JSONArray array = json.getJSONArray("notificationItems");
            for (int i = 0; i < array.size(); i++) {
                JSONObject item = array.getJSONObject(i);
                if (item.containsKey("NotificationRequestItem")) {
                    JSONObject request = item.getJSONObject("NotificationRequestItem");
                    if (request.containsKey("eventCode") && Objects.equals(request.getString("eventCode"), "AUTHORISATION")
                        && request.containsKey("success") && Objects.equals(request.getString("success"), "true")) {
                        code = 2;
                        if (request.containsKey("additionalData")) {
                            JSONObject additionalData = request.getJSONObject("additionalData");
                            if (additionalData.containsKey("metadata.OrderId")) {
                                String orderNo = additionalData.getString("metadata.OrderId");
                                if (!StringUtils.isEmpty(orderNo)) {
                                    orderId = Long.parseLong(orderNo.split("_")[0]);
                                }
                            }
                        }
                    }
                }
            }
        }
        if (orderId == null) {
            //未解析到订单ID
            return R.fail("OrderId not resolved");
        }

        if (!RedisUtils.tryLock("update_order_a_" + orderId, 10, 10)) {
            return R.fail("Order is updating");
        }

        //更新订单参数
        OrderUpdateBo bo = new OrderUpdateBo(orderId, code, null);

        PayOrder order = payOrderService.selectById(bo.getOrder_id());
        if (order == null) {
            return R.fail("Order does not exist");
        }

        //更新订单
        orderUpdateKernel.updateOrder(order, bo, json.toJSONString());
        RedisUtils.unlock("update_order_a_" + orderId);
        return R.ok();
    }

    /**
     * XiangPay支付回调
     *
     * @return String
     */
    @Log(title = "XiangPay支付回调", businessType = BusinessType.UPDATE)
    @RequestMapping("/updateXiang")
    public String updateXiang(XiangUpdateBo bo) {
        //更新订单
        if (!RedisUtils.tryLock("update_order_a_" + bo.getOrderId(), 10, 10)) {
            return "fail";
        }
        R<?> r = xiangPayOrderKernel.updateOrder(bo);
        RedisUtils.unlock("update_order_a_" + bo.getOrderId());
        return R.isSuccess(r) ? "ok" : "fail";
    }

    /**
     * updateCpCh2支付回调
     *
     * @return String
     */
    @Log(title = "updateCpCh2支付回调", businessType = BusinessType.UPDATE)
    @RequestMapping("/updateCpCh2")
    public String updateCpCh2(CpCh2UpdateBo bo) {
        //更新订单
        if (!RedisUtils.tryLock("update_order_a_" + bo.getOrder_no(), 10, 10)) {
            return "fail";
        }
        R<?> r = companyChannel2Kernel.updateOrder(bo);
        RedisUtils.unlock("update_order_a_" + bo.getOrder_no());
        return R.isSuccess(r) ? "200" : "fail";
    }

    /**
     * updateCpCh4支付回调
     *
     * @return String
     */
    @Log(title = "updateCpCh4支付回调", businessType = BusinessType.UPDATE)
    @RequestMapping("/updateCpCh4")
    public R<?> updateCpCh4(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        JSONObject bo = new JSONObject(parameterMap);
        System.out.println("updateCpCh4支付回调：" + bo.toJSONString());
        String order_id = parameterMap.get("order_id")[0];
        String is_success = parameterMap.get("is_success")[0];

        if (StringUtils.isEmpty(order_id) || StringUtils.isEmpty(is_success)) {
            return R.fail("params is valid");
        }

        return companyChannel4Kernel.updateOrder(order_id, Integer.parseInt(is_success));
    }

    /**
     * updateCpCh999支付回调
     *
     * @return String
     */
    @Log(title = "updateCpCh999支付回调", businessType = BusinessType.UPDATE)
    @PostMapping("/updateCpCh999")
    public R<?> updateCpCh999(@RequestBody JSONObject bo) {
        return companyChannel999Kernel.updateOrder(bo);
    }

    /**
     * power支付回调
     *
     * @return String
     */
    @Log(title = "power支付回调", businessType = BusinessType.UPDATE)
    @RequestMapping("/updatePower")
    public R<?> updatePower(PowerUpdateBo bo) {
        System.out.println("power支付回调：" + JSONObject.toJSONString(bo));
        return powerKernel.updateOrder(bo);
    }

    /**
     * account检查
     *
     * @return R
     */
    @Log(title = "account检查", businessType = BusinessType.OTHER)
    @PostMapping("/aoCheck")
    public R<?> aoCheck(@RequestBody JSONObject json, HttpServletRequest request) {
        String token = request.getHeader(OrderConstants.token_name);
        try {
            JwtUtils.parseToken(token);
        } catch (Exception ignore) {

        }

        System.out.println("account token:" + token);

        if (json.isEmpty() || !json.containsKey("ao") || !json.containsKey("msg")) {
            return R.fail("params is null");
        }
        String ao = json.getString("ao");
        String msg = json.getString("msg");

        String accountId;
        try {
            accountId = EncryptUtils.decryptBySm4(ao + "==", EncryptUtils.PRIVATE_KEY);
        } catch (Exception e) {
            try {
                accountId = EncryptUtils.decryptBySm4(ao, EncryptUtils.PRIVATE_KEY);
            } catch (Exception ignore) {
                return R.fail("ao error");
            }
        }

        if (StringUtils.isEmpty(accountId)) {
            return R.fail("ao is null");
        }

        PayAccount account = payAccountService.selectById(Long.parseLong(accountId));
        if (account == null) {
            return R.fail("ao is not exist");
        }
        if (account.getStatus() == 2) {
            return R.fail("ao is disable");
        }
        return orderUpdateKernel.accountBanDeal(account, msg);
    }

    /**
     * 补发订单邮件
     *
     * @return R
     */
    @Log(title = "补发订单邮件", businessType = BusinessType.OTHER)
    @PostMapping("/sendEmail")
    public R<?> sendEmail(@RequestBody JSONObject json) {

        if (json.isEmpty() || !json.containsKey("orderId")) {
            return R.fail("params is null");
        }

        String orderIds = json.getString("orderId");

        if (StringUtils.isEmpty(orderIds)) {
            return R.fail("orderId is null");
        }

        Arrays.asList(orderIds.split(",")).forEach(tmp -> {
            try {
                Long orderId = Long.parseLong(tmp);
                //发送支付成功邮件通知
                sendEmail2Kernel.sendOrderEmail(orderId);
            } catch (Exception e) {
                e.printStackTrace();
            }

        });

        return R.ok();
    }
}
